export default class CanvasPen {
  /**
   * 是否正在绘制中
   * */
  public running = false
  private ctx: CanvasRenderingContext2D | null

  constructor(private canvasRef: HTMLCanvasElement) {
    this.ctx = this.canvasRef.getContext('2d')
    this.running = true

    this.onResize()
    this.next()
  }

  onResize() {
    const domRect = this.canvasRef.getBoundingClientRect()
    this.canvasRef.width = domRect.width
    this.canvasRef.height = domRect.height
  }

  next() {
    requestAnimationFrame(() => {
      if (this.running) {
        this.pain()
      }
    })
  }

  /**
   * 一次绘图
   * */
  async pain() {
    const { ctx } = this

    if (ctx) {
      this.fillRect(ctx)
      this.drawPath(ctx)
      this.drawArc(ctx)
      this.drawArc2(ctx)
      this.drawRandomColorRecs(ctx)
      this.drawRandomLine(ctx)
      this.drawAlphaRect(ctx)
      this.drawRGBARect(ctx)
      this.drawLineStyle(ctx)
      this.drawGradient(ctx)
      await this.drawImage(ctx)
      this.drawShadow(ctx)
    }

    // this.next()
  }

  private drawShadow(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)
    ctx.shadowOffsetX = 3
    ctx.shadowOffsetY = 3
    ctx.shadowBlur = 5
    ctx.shadowColor = 'rgb(0 0 0 / 50%)'

    ctx.font = '20px Times New Roman'
    ctx.fillStyle = 'Black'
    ctx.fillText('Sample String', 5, 30)
  }

  private async drawImage(ctx: CanvasRenderingContext2D) {
    const imgSrc =
      ''

    ctx.translate(300, 0)

    return new Promise((resolve) => {
      const img = new Image()
      img.src = imgSrc
      img.onload = () => {
        const pattern = ctx.createPattern(img, 'repeat')
        if (pattern) {
          ctx.fillStyle = pattern
          ctx.fillRect(0, 0, 150, 150)
        }
        resolve(0)
      }
      img.onerror = resolve
    })
  }

  private drawGradient(ctx: CanvasRenderingContext2D) {
    ctx.translate(-1300, 200)
    const linGrad = ctx.createLinearGradient(0, 0, 0, 150)
    linGrad.addColorStop(0, '#00ABEB')
    linGrad.addColorStop(0.5, '#fff')
    linGrad.addColorStop(0.5, '#26C000')
    linGrad.addColorStop(1, '#fff')

    const linGrad2 = ctx.createLinearGradient(0, 50, 0, 95)
    linGrad2.addColorStop(0.5, '#000')
    linGrad2.addColorStop(1, 'rgb(0 0 0 / 0%)')

    // assign gradients to fill and stroke styles
    ctx.fillStyle = linGrad
    ctx.strokeStyle = linGrad2

    // draw shapes
    ctx.fillRect(10, 10, 130, 130)
    ctx.strokeRect(50, 50, 50, 50)

    ctx.translate(200, 0)
    const radGrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30)
    radGrad.addColorStop(0, '#A7D30C')
    radGrad.addColorStop(0.9, '#019F62')
    radGrad.addColorStop(1, 'rgb(1 159 98 / 0%)')

    const radGrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50)
    radGrad2.addColorStop(0, '#FF5F98')
    radGrad2.addColorStop(0.75, '#FF0188')
    radGrad2.addColorStop(1, 'rgb(255 1 136 / 0%)')

    const radGrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40)
    radGrad3.addColorStop(0, '#00C9FF')
    radGrad3.addColorStop(0.8, '#00B5E2')
    radGrad3.addColorStop(1, 'rgb(0 201 255 / 0%)')

    const radGrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90)
    radGrad4.addColorStop(0, '#F4F201')
    radGrad4.addColorStop(0.8, '#E4C700')
    radGrad4.addColorStop(1, 'rgb(228 199 0 / 0%)')

    // draw shapes
    ctx.fillStyle = radGrad4
    ctx.fillRect(0, 0, 150, 150)
    ctx.fillStyle = radGrad3
    ctx.fillRect(0, 0, 150, 150)
    ctx.fillStyle = radGrad2
    ctx.fillRect(0, 0, 150, 150)
    ctx.fillStyle = radGrad
    ctx.fillRect(0, 0, 150, 150)

    ctx.translate(200, 0)
    const conicGrad1 = ctx.createConicGradient(2, 62, 75)
    conicGrad1.addColorStop(0, '#A7D30C')
    conicGrad1.addColorStop(1, '#fff')

    const conicGrad2 = ctx.createConicGradient(0, 187, 75)
    // we multiply our values by Math.PI/180 to convert degrees to radians
    conicGrad2.addColorStop(0, 'black')
    conicGrad2.addColorStop(0.25, 'black')
    conicGrad2.addColorStop(0.25, 'white')
    conicGrad2.addColorStop(0.5, 'white')
    conicGrad2.addColorStop(0.5, 'black')
    conicGrad2.addColorStop(0.75, 'black')
    conicGrad2.addColorStop(0.75, 'white')
    conicGrad2.addColorStop(1, 'white')

    // draw shapes
    ctx.fillStyle = conicGrad1
    ctx.fillRect(12, 25, 100, 100)
    ctx.fillStyle = conicGrad2
    ctx.fillRect(137, 25, 100, 100)
  }

  private drawAlphaRect(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)
    // draw background
    ctx.fillStyle = '#FD0'
    ctx.fillRect(0, 0, 75, 75)
    ctx.fillStyle = '#6C0'
    ctx.fillRect(75, 0, 75, 75)
    ctx.fillStyle = '#09F'
    ctx.fillRect(0, 75, 75, 75)
    ctx.fillStyle = '#F30'
    ctx.fillRect(75, 75, 75, 75)
    ctx.fillStyle = '#FFF'

    // set transparency value
    ctx.globalAlpha = 0.2

    // Draw semi transparent circles
    for (let i = 0; i < 7; i++) {
      ctx.beginPath()
      ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true)
      ctx.fill()
    }
  }

  private drawRGBARect(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)
    ctx.globalAlpha = 1
    // Draw background
    ctx.fillStyle = 'rgb(255 221 0)'
    ctx.fillRect(0, 0, 150, 37.5)
    ctx.fillStyle = 'rgb(102 204 0)'
    ctx.fillRect(0, 37.5, 150, 37.5)
    ctx.fillStyle = 'rgb(0 153 255)'
    ctx.fillRect(0, 75, 150, 37.5)
    ctx.fillStyle = 'rgb(255 51 0)'
    ctx.fillRect(0, 112.5, 150, 37.5)

    // Draw semi transparent rectangles
    for (let i = 0; i < 10; i++) {
      ctx.fillStyle = `rgb(255 255 255 / ${(i + 1) / 10})`
      for (let j = 0; j < 4; j++) {
        ctx.fillRect(5 + i * 14, 5 + j * 37.5, 14, 27.5)
      }
    }
  }

  private drawPath(ctx: CanvasRenderingContext2D) {
    ctx.translate(100, 0)

    ctx.strokeStyle = 'rgb(0  10  10)'
    ctx.beginPath()
    ctx.moveTo(75, 50)
    ctx.lineTo(100, 75)
    ctx.lineTo(100, 25)
    ctx.closePath()
    ctx.stroke()

    ctx.fillStyle = 'rgb(200  100  0)'
    ctx.beginPath()
    ctx.moveTo(175, 50)
    ctx.lineTo(200, 75)
    ctx.lineTo(200, 25)
    ctx.closePath()
    ctx.fill()
  }

  private drawLineStyle(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)

    ctx.lineWidth = 1
    ctx.beginPath()
    ctx.moveTo(0, 0)
    ctx.lineTo(90, 0)
    ctx.stroke()

    ctx.lineWidth = 5
    ctx.beginPath()
    ctx.moveTo(110, 0)
    ctx.lineTo(200, 0)
    ctx.stroke()

    ctx.lineCap = 'round'
    ctx.beginPath()
    ctx.moveTo(0, 10)
    ctx.lineTo(90, 10)
    ctx.stroke()

    ctx.lineCap = 'square'
    ctx.beginPath()
    ctx.moveTo(110, 10)
    ctx.lineTo(200, 10)
    ctx.stroke()

    ctx.lineCap = 'butt'
    ctx.beginPath()
    ctx.moveTo(0, 20)
    ctx.lineTo(90, 20)
    ctx.stroke()

    ctx.lineJoin = 'round'
    ctx.beginPath()
    ctx.moveTo(0, 30)
    ctx.lineTo(100, 30)
    ctx.lineTo(100, 40)
    ctx.stroke()

    ctx.lineJoin = 'bevel'
    ctx.beginPath()
    ctx.moveTo(0, 50)
    ctx.lineTo(100, 50)
    ctx.lineTo(100, 60)
    ctx.stroke()

    ctx.lineJoin = 'miter'
    ctx.beginPath()
    ctx.moveTo(0, 70)
    ctx.lineTo(100, 70)
    ctx.lineTo(100, 80)
    ctx.stroke()

    ctx.miterLimit = 5.0
    ctx.beginPath()
    ctx.moveTo(0, 100)
    ctx.lineTo(100, 100)
    ctx.lineTo(100, 110)
    ctx.stroke()

    ctx.setLineDash([5, 5])
    ctx.beginPath()
    ctx.moveTo(0, 120)
    ctx.lineTo(100, 120)
    ctx.stroke()

    // Reset
    ctx.setLineDash([])
  }

  private drawRandomColorRecs(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)
    for (let i = 0; i < 6; i++) {
      for (let j = 0; j < 6; j++) {
        ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)} ${Math.floor(255 - 42.5 * j)} 0)`
        ctx.fillRect(j * 25, i * 25, 25, 25)
      }
    }
  }

  private drawRandomLine(ctx: CanvasRenderingContext2D) {
    for (let i = 0; i < 6; i++) {
      for (let j = 0; j < 6; j++) {
        ctx.strokeStyle = `rgb(0 ${Math.floor(255 - 42.5 * i)} ${Math.floor(255 - 42.5 * j)})`
        ctx.beginPath()
        ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, 2 * Math.PI, true)
        ctx.stroke()
      }
    }
  }

  private drawArc(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)

    ctx.strokeStyle = 'rgb(200  0  200)'
    ctx.beginPath()
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true) // Outer circle
    ctx.moveTo(110, 75)
    ctx.arc(75, 75, 35, 0, Math.PI, false) // Mouth (clockwise)
    ctx.moveTo(65, 65)
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true) // Left eye
    ctx.moveTo(95, 65)
    ctx.arc(90, 65, 5, 0, Math.PI * 2, true) // Right eye
    ctx.stroke()
  }

  private drawArc2(ctx: CanvasRenderingContext2D) {
    ctx.translate(200, 0)
    for (let iy = 0; iy < 4; iy++) {
      for (let ix = 0; ix < 3; ix++) {
        ctx.beginPath()
        const x = 25 + ix * 50 // x coordinate
        const y = 25 + iy * 50 // y coordinate
        const radius = 20 // Arc radius
        const startAngle = 0 // Starting point on circle
        const endAngle = Math.PI + (Math.PI * ix) / 2 // End point on circle
        const counterclockwise = iy % 2 !== 0 // clockwise or counterclockwise

        ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)

        if (iy > 1) {
          ctx.fill()
        } else {
          ctx.stroke()
        }
      }
    }
  }

  private fillRect(ctx: CanvasRenderingContext2D) {
    ctx.translate(10, 20)

    ctx.fillStyle = 'rgb(200 0 0)'
    ctx.fillRect(10, 10, 50, 50)

    ctx.fillStyle = 'rgb(0 0 200 / 50%)'
    ctx.fillRect(30, 30, 50, 50)
  }

  destroy() {
    this.running = false
    this.ctx = null
  }
}
